package com.easylinkin.linkappapi.meterbilling.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelReadUtil;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.meterbilling.entity.RoomInfo;
import com.easylinkin.linkappapi.meterbilling.mapper.RoomInfoMapper;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.space.entity.LinkappArea;
import com.easylinkin.linkappapi.space.service.LinkappAreaService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.meterbilling.entity.MeterDeviceConfig;
import com.easylinkin.linkappapi.meterbilling.mapper.MeterDeviceConfigMapper;
import com.easylinkin.linkappapi.meterbilling.service.MeterDeviceConfigService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

@Service
public class MeterDeviceConfigServiceImpl extends ServiceImpl<MeterDeviceConfigMapper, MeterDeviceConfig> implements MeterDeviceConfigService {
    private static final int PARSE_EXCEL_START_ROW = 1;
    private static final int CONTENT_IN_EXCEL_START_ROW = 1;
    private static final Logger LOGGER = LoggerFactory.getLogger(MeterDeviceConfigServiceImpl.class);

    @Resource
    LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    LinkappAreaService linkappAreaService;
    @Resource
    DeviceService deviceService;
    @Resource
    RoomInfoMapper roomInfoMapper;

    @Override
    public int batchInsert(List<MeterDeviceConfig> list) {
        return baseMapper.batchInsert(list);
    }

    /**
     * 分页
     * @param page
     * @param meterDeviceConfig
     * @return
     */
    @Override
    public Page getMeterDeviceConfigList(Page page, MeterDeviceConfig meterDeviceConfig){
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        meterDeviceConfig.setTenantId(tenantId);
        page.setRecords(baseMapper.getMeterDeviceConfigList(page,meterDeviceConfig));
        return page;
    }

    /***
     * 查询
     * @param meterDeviceConfig
     * @return
     */
    @Override
    public List<MeterDeviceConfig> getMeterDeviceConfigList(MeterDeviceConfig meterDeviceConfig){
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        meterDeviceConfig.setTenantId(tenantId);
        return baseMapper.getMeterDeviceConfigList(meterDeviceConfig);
    }

    /***
     * 住户获取水电表设备
     * @param meterDeviceConfig
     * @return
     */
    @Override
    public   List<MeterDeviceConfig> getDeviceAll(MeterDeviceConfig meterDeviceConfig){
        String phone = linkappUserContextProducer.getCurrentUsername();
        meterDeviceConfig.setPhone(phone);
        return baseMapper.getDeviceAll(meterDeviceConfig);
    }

    @Override
    public MeterDeviceConfig getMeterDeviceConfig(MeterDeviceConfig meterDeviceConfig){
        Assert.notNull(meterDeviceConfig,"参数不能为空!");
        Assert.notNull(meterDeviceConfig.getId(),"id 不能为空!");
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        meterDeviceConfig.setTenantId(tenantId);
        List<MeterDeviceConfig> meterDeviceConfigList = baseMapper.getMeterDeviceConfigList(meterDeviceConfig);
        if(meterDeviceConfigList == null || meterDeviceConfigList.size()<=0){
            return null;
        }
        return meterDeviceConfigList.get(0);
    }

    /***
     * 修改或者新增
     * @param meterDeviceConfig
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveEntity(MeterDeviceConfig meterDeviceConfig){
        Assert.notNull(meterDeviceConfig,"参数不能为空!");
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        meterDeviceConfig.setTenantId(tenantId);
        validParamFormat(meterDeviceConfig);
        boolean saveStatus= meterDeviceConfig.getId() == null ? true : false;
        checkRepeat(meterDeviceConfig,saveStatus);
        LocalDateTime date=LocalDateTime.now();
        if(saveStatus){
            meterDeviceConfig.setCreateTime(date);
        }
        meterDeviceConfig.setModifyTime(date);
        saveOrUpdate(meterDeviceConfig);
    }

    /***
     * 批量设置
     * @param meterDeviceConfig
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void  batchConfig(MeterDeviceConfig meterDeviceConfig){
        if(StringUtils.isEmpty(meterDeviceConfig) || meterDeviceConfig.getIds() == null || meterDeviceConfig.getIds().size()<=0){
            return;
        }
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        meterDeviceConfig.setTenantId(tenantId);
        UpdateWrapper<MeterDeviceConfig> meterDeviceConfigUpdateWrapper = new UpdateWrapper<>();
        boolean flag=false;
        if(meterDeviceConfig.getDayThreshold() != null){
            flag=true;
            meterDeviceConfigUpdateWrapper.set("day_threshold",meterDeviceConfig.getDayThreshold());
        }
        if(meterDeviceConfig.getMonthThreshold() != null){
            flag=true;
            meterDeviceConfigUpdateWrapper.set("month_threshold",meterDeviceConfig.getMonthThreshold());
        }
        meterDeviceConfigUpdateWrapper.eq("tenant_id",meterDeviceConfig.getTenantId());
        meterDeviceConfigUpdateWrapper.in("id",meterDeviceConfig.getIds());
        if(flag){
            update(meterDeviceConfigUpdateWrapper);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeIds(MeterDeviceConfig meterDeviceConfig){
        if(StringUtils.isEmpty(meterDeviceConfig) || meterDeviceConfig.getIds() == null || meterDeviceConfig.getIds().size()<=0){
            return;
        }
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        meterDeviceConfig.setTenantId(tenantId);
        UpdateWrapper<MeterDeviceConfig> meterDeviceConfigUpdateWrapper = new UpdateWrapper<>();
        meterDeviceConfigUpdateWrapper.set("delete_status",1);
        meterDeviceConfigUpdateWrapper.eq("tenant_id",meterDeviceConfig.getTenantId());
        meterDeviceConfigUpdateWrapper.in("id",meterDeviceConfig.getIds());
        update(meterDeviceConfigUpdateWrapper);
    }


    /***
     * 获取模板
     * @param request
     * @param response
     */
    @Override
    public void getImportTemplate(HttpServletRequest request, HttpServletResponse response) {
        String title = "水电表房间绑定模板";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            String keyValue = "设备类型:MeterType,设备编码:deviceCode,所属房间(空间层级用/划分):roomArea";
            ExcelTools
                    .exportExcel(outputStream, keyValue, inits(), ExcelConstant.XLS,
                            title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常");
        } catch (Exception e) {
            LOGGER.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！");
        }
    }

    public List<HashMap<String, Object>> inits(){
        return Arrays.asList( new HashMap<String,Object>(){{
            put("MeterType",MeterType.one.name );
            put("deviceCode","VIR1605337773899");
            put("roomArea","光谷步行街/光谷测试/GGT101");
        }});
    }



    /***
     * 导入
     * @param file xls文件
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<Integer, String> importData(MultipartFile file) {
        Map<Integer, String> deviceErrorMap = new HashMap<Integer, String>();
        List<MeterDeviceConfig> list = getImportData(getContentList(file), deviceErrorMap);
        if(list != null && list.size()>0){
            baseMapper.batchInsert(list);
        }
        return deviceErrorMap;
    }

    /***
     * 导出
     * @param meterDeviceConfig
     * @param request
     * @param response
     */
    @Override
    public void exportData(MeterDeviceConfig meterDeviceConfig, HttpServletRequest request, HttpServletResponse response) {
        List<MeterDeviceConfig> meterDeviceConfigList = this.getMeterDeviceConfigList(meterDeviceConfig);
        Assert.notEmpty(meterDeviceConfigList,"根据条件查得需要导出的数据为空");
        String keyValue = "设备编码:deviceCode,设备类型:MeterTypeName,所属房间:roomName";
        String title = "水电表管理";
        String fileName = title + ".xls";
        try {
            List<String> stringList = new ArrayList<String>();
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportBankExcelV2(outputStream, keyValue, meterDeviceConfigList, ExcelConstant.XLS, title, stringList);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败");
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败");
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    private List<MeterDeviceConfig> getImportData(List<List<String>> contentList, Map<Integer, String> deviceErrorMap) {
        MeterDeviceConfig meterDeviceConfig;
        String value;
        List<MeterDeviceConfig> meterDeviceConfigList = new ArrayList<>();
        LocalDateTime date=LocalDateTime.now();
        String tenantId=linkappUserContextProducer.getNotNullCurrent().getTenantId();
        for (int row = CONTENT_IN_EXCEL_START_ROW; row < contentList.size(); row++) {
            try {
                boolean loop = true;
                meterDeviceConfig = new MeterDeviceConfig();
                for (int i = 0; i < contentList.get(row).size() && loop; i++) {
                    value = org.apache.commons.lang3.StringUtils.isEmpty(contentList.get(row).get(i))
                            || org.apache.commons.lang3.StringUtils.isEmpty(contentList.get(row).get(i).trim())
                            ? null : contentList.get(row).get(i).trim();
                    switch (i) {
                        case 0:
                            Integer meterType=MeterType.getId(value);
                            if (meterType != null) {
                                meterDeviceConfig.setMeterType(meterType);
                            }else{
                                deviceErrorMap.put(row, "第" + row + "行，设备类型填写错误, 仅支持水表/电表");
                                loop = false;
                            }
                            break;
                        case 1:
                            Integer count=validateDeviceCode(value,tenantId);
                            if (count != null) {
                                meterDeviceConfig.setDeviceCode(value);
                            }else{
                                deviceErrorMap.put(row, "第" + row + "行，设备编码不存在系统中");
                                loop = false;
                            }
                            break;
                        case 2:
                            Integer roomId=validateRoom(value,tenantId);
                            if (roomId != null) {
                                meterDeviceConfig.setRoomId(roomId);
                            }else{
                                deviceErrorMap.put(row, "第" + row + "行，房间名称填写错误");
                                loop = false;
                            }
                            break;
                        default:
                            break;
                    }
                }
                if (loop) {
                    // 设置租户id
                    meterDeviceConfig.setTenantId(tenantId);
                    //增加之前再次效验
                    validParamFormat(meterDeviceConfig);
                    checkRepeat(meterDeviceConfig,true);
                    meterDeviceConfig.setCreateTime(date);
                    meterDeviceConfig.setModifyTime(date);
                    meterDeviceConfig.setDeleteStatus(0);
                    meterDeviceConfigList.add(meterDeviceConfig);
                }
            } catch (Exception e) {
                deviceErrorMap.put(row, "第" + row + "行" + e.getMessage());
                continue;
            }
        }
        return meterDeviceConfigList;
    }

    /***
     * 效验设备是否存在
     * @param deviceCode
     * @param tenantId
     * @return
     */
    private Integer validateDeviceCode(String deviceCode,String tenantId){
        if(StringUtils.isEmpty(deviceCode)){
            return null;
        }
        QueryWrapper<Device> queryWrapper = new QueryWrapper();
        queryWrapper.eq("code", deviceCode);
        queryWrapper.eq("tenant_id",tenantId);
        int count = deviceService.count(queryWrapper);
        if(count<=0){
            return null;
        }
        return count;
    }



    /***
     * 效验房间是否存在
     * @param roomId
     * @return
     */
    private Integer validateRoom(String roomId,String tenantId){
        if(StringUtils.isEmpty(roomId)){
            return null;
        }
        String[] rooms = roomId.split("/");
        String area=null;
        String room=null;
        if(rooms == null || rooms.length<=0){
            return null;
        }
        room=rooms[rooms.length-1];
        if(StringUtils.isEmpty(room)){
            return null;
        }
        // 检查房间是否存在
        QueryWrapper<RoomInfo> roomInfoQueryWrapper = new QueryWrapper<>();
        roomInfoQueryWrapper.eq("room_name",room);
        roomInfoQueryWrapper.eq("tenant_id",tenantId);
        roomInfoQueryWrapper.ne("delete_status",1);
        List<RoomInfo> roomInfoList = roomInfoMapper.selectList(roomInfoQueryWrapper);
        if(roomInfoList == null || roomInfoList.size()<=0){
            return null;
        }
        RoomInfo roomInfo = roomInfoList.get(0);
        if(rooms.length>=2) {
            //检查房间绑定关系是否存在
            StringBuffer sb=new StringBuffer();
            for (int i = 0; i < rooms.length - 1; i++) {
                sb.append(rooms[i]);
                if(i < rooms.length - 2){
                    sb.append(":");
                }
            }
            area=sb.toString();
            //判断房间绑定区域 是否和查询区域ID相同
            QueryWrapper<LinkappArea> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("tenant_id",tenantId);
            queryWrapper.eq("status",1);
            queryWrapper.eq("area_path",area);
            queryWrapper.select("id");
            List<LinkappArea> list = linkappAreaService.list(queryWrapper);
            if(list == null || list.size()<=0){
                return null;
            }
            LinkappArea linkappArea=list.get(0);
            //判断房间绑定区域 是否和查询区域ID相同
            roomInfo = null;
            for (int i = 0; i < roomInfoList.size(); i++) {
                if(!StringUtils.isEmpty(roomInfoList.get(i).getAreaId()) && !StringUtils.isEmpty(roomInfoList.get(i).getId()) &&
                        roomInfoList.get(i).getAreaId().equals(linkappArea.getId())){
                    roomInfo=roomInfoList.get(i);
                }
            }
        }
        if(StringUtils.isEmpty(roomInfo)){
            return null;
        }
        return roomInfo.getId();
    }

    private List<List<String>> getContentList(MultipartFile file) {

        List<List<String>> contentList = ExcelReadUtil
                .getExcelInfo(file, PARSE_EXCEL_START_ROW, null, -1);

        //验证导入文件 是否使用了正确的模板，以及表格第一列是否缺失或多了字段
        Map<Integer, String> standardHeaderRow = new HashMap<>(6);

        String keyValue = "设备类型:MeterTypeName,设备编码:deviceCode,所属房间(空间层级用/划分):roomName";
        List<String> keys = Arrays.asList(org.apache.commons.lang3.StringUtils.split(keyValue, ",")).stream().map(s -> s = s.substring(0, s.indexOf(":"))).collect(Collectors.toList());
        for (int i = 0; i < keys.size(); i++) {
            standardHeaderRow.put(i, keys.get(i));
        }

        org.apache.commons.lang3.StringUtils.split(",");
        //根据表头判断是否使用了正确的模板
        ExcelReadUtil.validateExeclTemplet(contentList, 0, standardHeaderRow);

        int[] requiredColumnIndexes = {0, 1, 2};

        int errorNum = ExcelReadUtil.validateRequiredFields(contentList, requiredColumnIndexes);
        if (errorNum != -1) {
            throw new RuntimeException("第" + (errorNum + CONTENT_IN_EXCEL_START_ROW) + "行,必填写列为空");
        }
        return contentList;
    }


    /***
     * 参数必填效验
     * @param meterDeviceConfig
     */
    private void validParamFormat(MeterDeviceConfig meterDeviceConfig){
        Assert.notNull(meterDeviceConfig,"参数不能为空!");
        Assert.notNull(meterDeviceConfig.getRoomId(),"roomId 不能为空!");
        Assert.notNull(meterDeviceConfig.getDeviceCode(),"deviceCode 不能为空!");
        Assert.notNull(meterDeviceConfig.getMeterType(),"MeterType 不能为空!");
    };

    /***
     *  参数格式效验
     * @param meterDeviceConfig 效验参数
     * @param type true:新增  false:修改
     */
    private void checkRepeat(MeterDeviceConfig meterDeviceConfig,boolean type){
        //检查设备是否存在
        Integer validateDeviceCode = validateDeviceCode(meterDeviceConfig.getDeviceCode(), meterDeviceConfig.getTenantId());
        Assert.notNull(validateDeviceCode,"设备"+meterDeviceConfig.getDeviceCode()+"不存在");

        //检查房间是否存在
        RoomInfo roomInfo = roomInfoMapper.selectById(meterDeviceConfig.getRoomId());
        if(StringUtils.isEmpty(roomInfo) || roomInfo.getDeleteStatus().equals(1)){
            throw new IllegalArgumentException("该房间"+meterDeviceConfig.getRoomId()+"不存在");
        }

        QueryWrapper<MeterDeviceConfig> meterDeviceConfigQueryWrapper = new QueryWrapper<>();
        meterDeviceConfigQueryWrapper.eq("tenant_id",meterDeviceConfig.getTenantId());
        meterDeviceConfigQueryWrapper.eq("device_code",meterDeviceConfig.getDeviceCode());
        meterDeviceConfigQueryWrapper.ne("delete_status",1);
        List<MeterDeviceConfig> meterDeviceConfigs = baseMapper.selectList(meterDeviceConfigQueryWrapper);
        if(meterDeviceConfigs != null && meterDeviceConfigs.size()>0 &&
                ((type == false && !meterDeviceConfig.getId().equals(meterDeviceConfigs.get(0).getId())) || type)){
            roomInfo = roomInfoMapper.selectById(meterDeviceConfigs.get(0).getRoomId());
            throw new IllegalArgumentException(meterDeviceConfig.getDeviceCode()+"设备已被"+roomInfo.getRoomName()+"房间绑定");
        }

        //检查房间是否已存在设备
        meterDeviceConfigQueryWrapper = new QueryWrapper<>();
        meterDeviceConfigQueryWrapper.eq("tenant_id",meterDeviceConfig.getTenantId());
        meterDeviceConfigQueryWrapper.eq("room_id",meterDeviceConfig.getRoomId());
        meterDeviceConfigQueryWrapper.ne("delete_status",1);
        meterDeviceConfigs = baseMapper.selectList(meterDeviceConfigQueryWrapper);
        if(meterDeviceConfigs == null || meterDeviceConfigs.size()<=0){
            return;
        }
        if(meterDeviceConfigs.size() >= 2 && type){
            Set<String> collect = meterDeviceConfigs.stream().map(i -> i.getDeviceCode()).collect(Collectors.toSet());
            throw new IllegalArgumentException("该房间已绑定"+collect.toString()+"设备");
        }
        meterDeviceConfigs.forEach(meterDeviceConfigOld->{
            if(!StringUtils.isEmpty(meterDeviceConfigOld.getMeterType()) && !StringUtils.isEmpty(meterDeviceConfig.getMeterType()) &&
                    meterDeviceConfig.getMeterType().equals(meterDeviceConfigOld.getMeterType()) &&
                    !meterDeviceConfigOld.getId().equals(meterDeviceConfig.getId())){
                //新增设备与原有设备类型相同
                throw new IllegalArgumentException("该房间已绑定相同类型设备"+meterDeviceConfigOld.getDeviceCode());
            }
        });
    }

    enum MeterType{
        zero(0,"水表"),
        one(1,"电表");

        private Integer id;
        private String name;
        MeterType(Integer id,String name){
            this.id=id;
            this.name=name;
        }
        static public Integer getId(String name){
            if(StringUtils.isEmpty(name)){
                return  null;
            }
            for (MeterType MeterType : MeterType.values()) {
                if(MeterType.name.equals(name)){
                    return MeterType.id;
                }
            }
            return null;
        }
        static public String getName(Integer id){
            for (MeterType MeterType : MeterType.values()) {
                if(MeterType.id.equals(id)){
                    return MeterType.name();
                }
            }
            return "未知类型";
        }
    }
}
